package com.caishi.lkx.order.api;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.caishi.lkx.common.BaseApi;
import com.caishi.lkx.common.config.EncryptionKerayHandlerMethodArgumentResolverConfig;
import com.caishi.lkx.common.context.RequireContext;
import com.caishi.lkx.common.fliter.ContextInterceptor;
import com.caishi.lkx.common.ienum.DeviceType;
import com.caishi.lkx.common.role.Role;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.common.util.HttpContextUtil;
import com.caishi.lkx.exam.ienum.status.ProductCodeStatus;
import com.caishi.lkx.exam.ienum.type.ProductCodeType;
import com.caishi.lkx.exam.model.ColumnModel;
import com.caishi.lkx.exam.model.PackageModel;
import com.caishi.lkx.exam.model.PackageSkuModel;
import com.caishi.lkx.exam.model.ProductCodeModel;
import com.caishi.lkx.exam.records.RecordsGetDataService;
import com.caishi.lkx.exam.records.dto.RecordsCountRecordDto;
import com.caishi.lkx.exam.service.*;
import com.caishi.lkx.order.vo.OrderAndPaymentVo;
import com.caishi.lkx.user.model.AgentUserModel;
import com.caishi.lkx.user.model.IUserModel;
import com.caishi.lkx.user.model.UserModel;
import com.caishi.lkx.user.service.IAgentUserService;
import com.caishi.lkx.user.service.IGroupService;
import com.caishi.lkx.user.service.IUserService;
import com.zzw.common.Wrappers;
import com.zzw.common.exception.BizException;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.lock.RedissonLock;
import com.zzw.common.utils.CommonUtil;
import com.zzw.common.utils.UUIDUtil;

import com.caishi.lkx.order.ienum.status.OrderStatus;
import com.caishi.lkx.order.ienum.status.PaymentStatus;
import com.caishi.lkx.order.ienum.type.AssetsType;
import com.caishi.lkx.order.ienum.type.OrderType;
import com.caishi.lkx.order.ienum.type.PaymentType;
import com.caishi.lkx.order.model.OrderDetailModel;
import com.caishi.lkx.order.model.OrderModel;
import com.caishi.lkx.order.model.PaymentModel;
import com.caishi.lkx.order.model.vo.OpenSkuVo;
import com.caishi.lkx.order.service.IOrderDetailService;
import com.caishi.lkx.order.service.IOrderService;
import com.caishi.lkx.order.service.IPaymentService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * @Author huangpeng
 * @Description
 * @Date 2022/1/17 13:32
 **/
@Tag(name = "订单相关接口")
@RestController
@RequestMapping("/order/or/order")
@Slf4j
public class OrderApi extends BaseApi {
    @Resource
    private IOrderService orderService;
    @Resource
    private IOrderDetailService orderDetailService;
    @Resource
    private IAgentUserService agentUserService;
    @Resource
    private IUserService<UserModel> userService;
    @Resource
    private OrderApi owner;
    @Resource
    private IPaymentService paymentService;
    @Resource
    private IPackageSkuService skuService;
    @Resource
    private IPackageService packageService;
    @Resource
    private IIndustryService industryService;
    @Resource
    private IExamService examService;
    @Resource
    private IPackageSkuService packageSkuService;
    @Resource
    private IProductCodeService productCodeService;
    @Resource
    private IProductBaseService productBaseService;
    @Resource
    private IGroupService groupService;
    @Resource
    private ICategoryService categoryService;

    @Resource
    private RecordsGetDataService recordsGetDataService;
    @Resource
    private ContextInterceptor contextInterceptor;

    @GetMapping("/page")
    @Roles({Role.admin})
    @RequireContext({com.caishi.lkx.common.context.Role.CHANNEL,com.caishi.lkx.common.context.Role.ADMIN})
    public IPage<Object> page(@ModelAttribute Page<OrderModel> page,
                              @ModelAttribute OrderModel orderModel,
                              String mobile,
                              String assetsName,
                              String orderSn,
                              PaymentStatus paymentStatus,
                              @RequestParam(required = false) String industryId,
                              @RequestParam(required = false) String examId,
                              @RequestParam(required = false) String packageId,
                              @RequestParam(required = false) LocalDateTime createdStartTime,
                              @RequestParam(required = false) LocalDateTime createdEndTime,
                              @RequestParam(required = false) LocalDateTime payStartTime,
                              @RequestParam(required = false) LocalDateTime payEndTime,
                              @RequestParam(defaultValue = "false") Boolean detail,
                              @RequestParam(defaultValue = "false") Boolean payment,
                              @RequestParam(defaultValue = "false") Boolean paymentUser,
                              @RequestParam(defaultValue = "false") Boolean productDetail,
                              @RequestParam(defaultValue = "false") Boolean createdDetail, // 创建人详情
                              @RequestParam(defaultValue = "false") Boolean orderMasterDetail, // 订单拥有者详情
                              @RequestParam(required = false) PaymentType paymentType,
                              @RequestParam(required = false) DeviceType deviceType,
                              @RequestParam(required = false,defaultValue = "false") Boolean isDistribute

    ) {

        var groupId = context.getGroupId();

        Map<String, Object> query = orderModel.toMap();
        query.put("paymentStatus", ObjectUtil.isNotEmpty(paymentStatus) ? paymentStatus.getCode() : null);
        query.put("createdStartTime", createdStartTime);
        query.put("createdEndTime", createdEndTime);
        query.put("payStartTime", payStartTime);
        query.put("payEndTime", payEndTime);
        query.put("orderSn", orderSn);
        query.put("industryId", industryId);
        query.put("examId", examId);
        query.put("packageId", packageId);
        query.put("paymentType", paymentType);
        query.put("deviceType", deviceType);
        query.put("groupId", groupId);
        query.put("isDistribute", isDistribute);

        if (StrUtil.isNotBlank(mobile)) {
            query.put("userId", userService.getUserIdByMobile(mobile));
        }
        var orderList = orderService.search(clearReturnEmptyStr(query), page, true);
        IPage<Object> result = orderList.convert(v -> v);
        result.setRecords(orderList.getRecords().parallelStream().map(o -> orderListProcess(o, detail, payment, paymentUser, productDetail, createdDetail, orderMasterDetail)).collect(Collectors.toList()));
        return result;
    }


    @GetMapping("/countAmount")
    @Roles({Role.admin})
    @RequireContext({com.caishi.lkx.common.context.Role.CHANNEL})
    public Long countAmount(@ModelAttribute Page<Object> page,
                            @ModelAttribute OrderModel orderModel,
                            String mobile,
                            String assetsName,
                            String orderSn,
                            PaymentStatus paymentStatus,
                            @RequestParam(required = false) String industryId,
                            @RequestParam(required = false) String examId,
                            @RequestParam(required = false) String packageId,
                            @RequestParam(required = false) LocalDateTime createdStartTime,
                            @RequestParam(required = false) LocalDateTime createdEndTime,
                            @RequestParam(required = false) LocalDateTime payStartTime,
                            @RequestParam(required = false) LocalDateTime payEndTime,
                            @RequestParam(defaultValue = "false") Boolean detail,
                            @RequestParam(defaultValue = "false") Boolean payment,
                            @RequestParam(defaultValue = "false") Boolean paymentUser,
                            @RequestParam(defaultValue = "false") Boolean productDetail,
                            @RequestParam(defaultValue = "false") Boolean createdDetail, // 创建人详情
                            @RequestParam(defaultValue = "false") Boolean orderMasterDetail // 订单拥有者详情

    ) {
        var groupId = context.getGroupId();
        Map<String, Object> query = orderModel.toMap();
        query.put("paymentStatus", ObjectUtil.isNotEmpty(paymentStatus) ? paymentStatus.getCode() : null);
        query.put("createdStartTime", createdStartTime);
        query.put("createdEndTime", createdEndTime);
        query.put("orderSn", orderSn);
        query.put("payStartTime", payStartTime);
        query.put("payEndTime", payEndTime);
        query.put("industryId", industryId);
        query.put("examId", examId);
        query.put("packageId", packageId);
        query.put("groupId", groupId);
        if (StrUtil.isNotBlank(mobile)) {
            query.put("userId", userService.getUserIdByMobile(mobile));
        }
        return orderService.orderAmount(clearReturnEmptyStr(query));
    }

    private Object orderListProcess(OrderModel order,
                                    boolean detail, // 订单类容详情
                                    boolean payDetail, // 订单支付详情
                                    boolean paymentUser, // 订单支付用户
                                    boolean productDetail, // 订单商品详情
                                    boolean createdDetail, // 创建人详情
                                    boolean orderMasterDetail // 订单拥有者详情
    ) {
        order = orderService.orderDetail(order, true, true, payDetail);
        var vo = new OrderAndPaymentVo();
        OrderDetailModel oneDetail = order.getOneDetail();
        //正常情况下不会出现 oneDetail不会为空
        boolean skuFlag = true;
        skuF:
        if (oneDetail != null) {
            PackageSkuModel sku = (PackageSkuModel) oneDetail.getAssets();
            if (null == sku) {
                skuFlag = false;
                break skuF;
            }
            AtomicLong tmPrice = new AtomicLong();
            AtomicLong price = new AtomicLong();
            AtomicLong discountPrice = new AtomicLong();
            AtomicLong agentPrice = new AtomicLong();
            order.getOrderDetail().forEach(f -> {
                PackageSkuModel skuModel = (PackageSkuModel) f.getAssets();
                if (null == skuModel) return;
                tmPrice.addAndGet(skuModel.getTmPrice());
                price.addAndGet(skuModel.getPrice());
                discountPrice.addAndGet(skuModel.getDiscountPrice());
                agentPrice.addAndGet(skuModel.getAgentPrice());
            });
            vo.setSkuName(sku.getName());
            vo.setPackageName(packageService.queryName(sku.getPackageId()));
            vo.setExamName(examService.queryName(sku.getExamId()));
            vo.setIndustryName(industryService.queryName(sku.getIndustryId()));
            vo.setSkuOriginalPrice(price.get());
            vo.setSkuDiscountPrice(discountPrice.get());
            vo.setSkuAgentPrice(agentPrice.get());
            vo.setSkuTmPrice(tmPrice.get());
        }
        if (null == oneDetail || !skuFlag) {
            vo.setSkuName("已被删除");
            vo.setPackageName("已被删除");
            vo.setExamName("已被删除");
            vo.setIndustryName("已被删除");
            vo.setSkuOriginalPrice(0L);
            vo.setSkuDiscountPrice(0L);
            vo.setSkuAgentPrice(0L);
            vo.setSkuTmPrice(0L);
        }
        BeanUtil.copyProperties(order, vo);
        Map<String, Object> map = BeanUtil.beanToMap(vo);
        if (payDetail) {
            PaymentModel paymentModel = order.getPaymentModel();
            map.put("paymentModel", paymentModel);
            if (paymentUser && paymentModel != null) {
                map.put("paymentUser", userService.userSimpleMsg(paymentModel.getPayUserId()));
            }
        }
        if (createdDetail) {
            map.put("createdUser", userService.userSimpleMsg(order.getCreatedBy()));
        }
        if (orderMasterDetail) {
            map.put("master", userService.userSimpleMsg(order.getUserId()));
        }
        return map;
    }

    @Operation(summary = "获取订单详情")
    @GetMapping("/user-order-detail/{orderId}")
    public OrderModel userOrderDetail(@PathVariable String orderId,
                                      @RequestParam(defaultValue = "true") Boolean productDetail,
                                      @RequestParam(defaultValue = "true") Boolean payDetail
    ) {
        return orderService.orderDetail(orderId, productDetail, productDetail, payDetail);
    }


    @Operation(summary = "代理商开课")
    @PostMapping("/agent/open-sku")
    @Parameters({
            @Parameter(name = "skuId", required = true, description = "skuId"),
            @Parameter(name = "userPhone", required = true, description = "用户手机号"),
            @Parameter(name = "email", description = "邮箱")})
    //@RequireContext(com.caishi.lkx.common.context.Role.CHANNEL)
    public Boolean agentOpenCourse(@RequestParam String skuId, @RequestParam List<String> userPhone, HttpServletRequest request) {
        String userId = userContext.currentUserId();
        //sku金额
        PackageSkuModel sku = packageSkuService.getById(skuId);
        //代理商折扣
        AgentUserModel mainInfo = agentUserService.getMainInfo(userId);
        //支付金额
        long price = new BigDecimal(sku.getAgentPrice()).multiply(mainInfo.getDiscount()).longValue();
        var okPhones = new CopyOnWriteArrayList<String>();
        var copy = new LinkedList<>(userPhone);
        var t = userContext.currentTokenData().getDevice();
        try {
            RedissonLock.lock(userId + ":agent-open-course", k -> {
                // 创建订单
                userService.selectListByPhoneOrAdd(userPhone, userId)
                        .stream()
                        .forEach(u -> {
                            owner.agentOpenCourseSingle(userId, skuId, u, mainInfo.getId(), t, price, mainInfo.getBindGroupId());
                            okPhones.add(u.getMobile());
                        });

            });
        } catch (Exception e) {
            e.printStackTrace();
            Throwable cause = e.getCause();
            String message = e.getMessage();
            if (cause instanceof BizRuntimeException) {
                message = e.getCause().getMessage();
            }
            copy.removeAll(okPhones);
            log.error("代理商开课失败:userId:{},payUserId:{},failPhones:{},message:{}", userId, mainInfo.getId(), userPhone, message);
            throw new BizRuntimeException("手机号:" + copy + "开课失败:" + message);
        }

        return true;
    }

    @Operation(summary = "代理商提码 套餐id")
    @PostMapping("/agent/open-code")
    @Parameters({
            @Parameter(name = "packageId", required = true, description = "packageId"),
            @Parameter(name = "email", description = "邮箱")})
    @Transactional(rollbackFor = Exception.class)
    public Boolean agentOpenCode(@RequestParam String packageId, HttpServletRequest request) {
        String userId = userContext.currentUserId();
        //sku金额
        PackageModel packageModel = packageService.getById(packageId);
        //代理商折扣
        AgentUserModel mainInfo = agentUserService.getMainInfo(userId);
        //支付金额
        long price = new BigDecimal(packageModel.getAgentPrice()).multiply(mainInfo.getDiscount()).longValue();
        ProductCodeModel codeModel = new ProductCodeModel();
        codeModel.setCreatedBy(userId);
        codeModel.setId(UUIDUtil.generateUUIDByTimestamp());
        codeModel.setPackageId(packageId);
        codeModel.setIndustryId(packageModel.getIndustryId());
        codeModel.setExamId(packageModel.getExamId());
        try {
            RedissonLock.lock(userId + ":agent-open-code", k -> {
                // 创建订单
                OrderModel orderModel = createOrder(packageId, userId, price, codeModel);
                productCodeService.insert(codeModel);
                // 生成支付单
                paymentService.getPaySign(mainInfo.getId(), PaymentType.agentpay, orderModel.getId(), HttpContextUtil.currentDeviceType(request), null, null);
                // 订单直接支付完成
                paymentService.paySuccess(orderModel.getId(), null,null);
                orderService.orderSuccess(orderModel.getId());
            });
        } catch (Exception e) {
            Throwable cause = e.getCause();
            String message = e.getMessage();
            if (cause instanceof BizRuntimeException) {
                message = e.getCause().getMessage();
            }
            log.error("代理商开课失败:userId:{},payUserId:{},message:{}", userId, mainInfo.getId(), message);
            throw new BizRuntimeException("提码失败:" + message);
        }

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean agentOpenCourseSingle(String userId, String skuId, UserModel user, String agentId, DeviceType type, Long price, Long groupId) {
        OrderModel orderModel = orderService.createOrder(userId, user.getId(), OrderType.openCourse, Collections.singletonList(OrderDetailModel.builder()
                .entityId(skuId)
                .entityType(AssetsType.product)
                .totalCount(1)
                .build()), null, PaymentType.agentpay, null, price, skuId, groupId);
        // 生成支付单
        paymentService.getPaySign(agentId, PaymentType.agentpay, orderModel.getId(), type, null, null);
        // 订单直接支付完成
        paymentService.paySuccess(orderModel.getId(), null,null);
        orderService.orderSuccess(orderModel.getId());
        return true;
    }

    @Operation(summary = "代理商开课记录")
    @GetMapping("/agent/open-page")
    @Roles({Role.onlyLogin})
    @RequireContext(value = com.caishi.lkx.common.context.Role.CHANNEL)
    public IPage<OrderModel> agentOpenCourse(@ModelAttribute Page<OrderModel> page,
                                             @RequestParam(required = false, defaultValue = "false") Boolean all,
                                             @RequestParam(required = false) Boolean showDetail,
                                             @RequestParam(required = false) String userId,
                                             @RequestParam(required = false) OrderStatus orderStatus,
                                             @RequestParam(required = false) OrderType type,
                                             @RequestParam(required = false) LocalDateTime startTime,
                                             @RequestParam(required = false) LocalDateTime endTime,
                                             @RequestParam(required = false) String industryId,
                                             @RequestParam(required = false) String examId,
                                             @RequestParam(required = false) String mobile,
                                             @RequestParam(required = false) String code
    ) {
        List<String> userIds = getUserIds(all, userId);
        if (userIds == null || userIds.isEmpty())
            return page;

        IPage<OrderModel> orderModelPage;
        if (type.equals(OrderType.openCourse)) {
            Map<String, Object> map = new HashMap<>();
            map.put("userIds", !all ? userIds : (userIds.isEmpty() ? null : userIds));
            map.put("startTime", startTime);
            map.put("endTime", endTime);
            map.put("orderStatus", orderStatus);
            map.put("industryId", industryId);
            map.put("examId", examId);
            map.put("mobile", mobile);
            orderModelPage = orderService.openCourseList(page, map);
        } else if (type.equals(OrderType.openCode)) {
            Map<String, Object> map = MapUtil.<String, Object>builder().put("userIds", !all ? userIds : (userIds.isEmpty() ? null : userIds))
                    .put("startTime", startTime)
                    .put("endTime", endTime)
                    .put("orderStatus", orderStatus)
                    .put("code", code).build();
            orderModelPage = orderService.openCodeList(page, map);

        } else {
            orderModelPage = orderService.agentOpenCourse(page, userIds, userId, orderStatus, startTime, endTime, type);
        }
        boolean isCode = false;
        if (OrderType.openCode.equals(type)) {
            isCode = true;
        }
        orderModelPage.setRecords(agentOrderConver(orderModelPage.getRecords(), isCode));
        return orderModelPage;
    }

    @Operation(summary = "代理商消费记录")
    @GetMapping("/agent/money")
    @Roles({Role.onlyLogin})
    public Long agentMoney(@RequestParam(required = false, defaultValue = "false") Boolean all,
                           @RequestParam(required = false) String userId,
                           @RequestParam(required = false) OrderStatus orderStatus,
                           @RequestParam(required = false) OrderType type,
                           @RequestParam(required = false) LocalDateTime startTime,
                           @RequestParam(required = false) LocalDateTime endTime
    ) {
        List<String> userIds = getUserIds(all, userId);
        if (userIds == null || userIds.isEmpty())
            return 0L;
        return orderService.agentMoney(userIds, userId, orderStatus, startTime, endTime, type);
    }

    public List<String> getUserIds(Boolean all, String userId) {
        List<String> userIds = new ArrayList<>();
        AgentUserModel agentuser;
        if (!all) {
            if (context.getGroupId() != null && context.getGroupId() != 0L) {
                List<String> list = agentUserService.selectList(Wrappers.<AgentUserModel>lambdaQuery()
                        .eq(AgentUserModel::getBindGroupId, context.getGroupId()).select(AgentUserModel::getId)).parallelStream().map(AgentUserModel::getId).toList();

                userIds.addAll(list);
                List<String> finalUserIds = userIds;
                list.forEach(id -> finalUserIds.addAll(agentUserService.selectNextChildrenIds(id)));
                userIds.addAll(finalUserIds);
                if (userIds.isEmpty()) {
                    userIds.add(userContext.currentUserId());
                }
            } else {
                agentuser = agentUserService.getById(userContext.currentUserId());
                if (null == agentuser) {
                    return null;

                }
                var parenrId = agentuser.getId();
                if (StrUtil.isNotBlank(agentuser.getParentId()))
                    parenrId = agentuser.getParentId();
                userIds = agentUserService.selectNextChildrenIds(parenrId);
                userIds.add(parenrId);
            }

        } else {
            if (StrUtil.isNotBlank(userId)) {
                agentuser = agentUserService.getById(userId);
                if (null == agentuser) {
                    return null;
                }
                userIds = agentUserService.selectNextChildrenIds(agentuser.getId());
                userIds.add(agentuser.getId());
            }
        }
        return userIds;
    }

    ;

    public List<OrderModel> agentOrderConver(List<OrderModel> orders, Boolean isCode) {
        if (isCode) {
            return orders.stream().peek(o -> {
                o.setCreatedByName(userService.getUserNameById(o.getCreatedBy()));
                OrderDetailModel orderDetailModel = orderDetailService.selectFirst(Wrappers.<OrderDetailModel>lambdaQuery().eq(OrderDetailModel::getOrderId, o.getId()));
                ProductCodeModel productCode = productCodeService.getById(orderDetailModel.getEntityId());
                if (null != productCode) {
                    if (StrUtil.isNotBlank(productCode.getUserId())) {
                        productCode.setUserName(userService.getUserNameById(productCode.getUserId()));
                    }
                    PackageModel packageModel = packageService.getById(orderDetailModel.getEntityId());
                    if (packageModel != null) {
                        o.setEntityName(packageModel.getName());
                    }
                    o.setEntityName(packageService.queryName(productCode.getPackageId()));
                    if (productCode.getSkuId() != null) {
                        productCode.setSkuName(packageSkuService.queryName(productCode.getSkuId()));
                    }
                    if (productCode.getUserId() != null) {
                        productCode.setUserName(userService.queryName(productCode.getUserId()));
                    }
                    o.setExtr(productCode);
                } else {
                    o.setExtr(new ProductCodeModel());
                }
//                if (OrderType.openCourse.equals(o.getType()) && StrUtil.isNotBlank(o.getEntityId())) {
//                    o.setEntityName(packageSkuService.queryName(o.getEntityId()));
//                } else if (OrderType.openCode.equals(o.getType()) && StrUtil.isNotBlank(o.getEntityId())) {
//                    o.setEntityName(packageService.queryName(o.getEntityId()));
//                }

            }).toList();
        }
        return orders.parallelStream().peek(o -> {
            o.setCreatedByName(userService.getUserNameById(o.getCreatedBy()));
            IUserModel userDetail = userService.getUserDetail(o.getUserId());
            OrderDetailModel orderDetailModel = orderDetailService.selectFirst(Wrappers.<OrderDetailModel>lambdaQuery().eq(OrderDetailModel::getOrderId, o.getId()));

            if (null != userDetail) {
                o.setUserMobile(CommonUtil.mobileSafe(userDetail.getMobile()));
                o.setUserName(userDetail.getName());
            }
            if (OrderType.openCourse.equals(o.getType()) && StrUtil.isNotBlank(orderDetailModel.getEntityId())) {
                o.setEntityName(packageSkuService.queryName(orderDetailModel.getEntityId()));
            }
//            else if (OrderType.openCode.equals(o.getType()) && StrUtil.isNotBlank(o.getEntityId())) {
////                o.setEntityName(packageService.queryName(o.getEntityId()));
////            }

        }).collect(Collectors.toList());
    }

    @Operation(summary = "机构开课订单退款")
    @PostMapping("/agent/refuse")
    @Transactional(rollbackFor = Exception.class)
    @Roles({Role.onlyLogin})
    public boolean agentRefund(@RequestParam String id) throws BizException {
        orderService.applyRefund(id, userContext.currentUserId(), false, true);
        orderService.orderConfirmRefund(id, null);
        return true;
    }

    @Operation(summary = "代理商订单详情")
    @GetMapping("/agent/open-detail")
    @Transactional(rollbackFor = Exception.class)
    @Roles({Role.onlyLogin})
    public List<ColumnModel> agentOrderDetail(@RequestParam String orderId) {
        List<ColumnModel> list = Collections.synchronizedList(new ArrayList<>());
        orderDetailService.selectOrderDetails(orderId).parallelStream().forEach(detailModel -> {
            if (AssetsType.skuCode.equals(detailModel.getEntityType())) {
                List<ColumnModel> columnModels = productCodeService.getColumnList(detailModel.getEntityId());
                if (CollUtil.isNotEmpty(columnModels)) {
                    list.addAll(columnModels);
                }
            } else if (AssetsType.product == detailModel.getEntityType()) {
                list.addAll(packageSkuService.getEffecitiveColums(detailModel.getEntityId()));
            }
        });
        return list.stream().peek(c -> {
            c.setCategoryName(categoryService.queryName(c.getCategoryId()));
        }).toList();
    }

    @Operation(summary = "代理商开课详情")
    @GetMapping("/agent/open-sku-detail")
    @Roles({Role.onlyLogin})
    public OpenSkuVo openSkuDetail(@RequestParam String orderId) {
        OpenSkuVo openSkuVo = new OpenSkuVo();
        //获取Sku
        OrderModel orderModel = orderService.orderDetail(orderId, true, true, true);
        OrderDetailModel oneDetail = orderModel.getOneDetail();
        PackageSkuModel packageSkuModel = packageSkuService.getById(oneDetail.getEntityId());
        //获取套餐名
        PackageModel packageModel = packageService.getById(packageSkuModel.getPackageId());
        openSkuVo.setOrderId(orderId);
        openSkuVo.setIndustryName(industryService.queryName(packageModel.getIndustryId()));
        openSkuVo.setExamName(examService.queryName(packageModel.getExamId()));
        openSkuVo.setPackageName(packageModel.getName());
        List<Long> categoryIds = packageSkuModel.getCategoryIds();
        List<String> categoryName = categoryIds.parallelStream().map(m -> categoryService.queryName(m)).toList();
        openSkuVo.setCategoryName(categoryName);
        openSkuVo.setAgentPrice(orderModel.getPaymentModel().getPaymentAmount());
        openSkuVo.setPrice(packageSkuModel.getAgentPrice());
        String createdBy = orderModel.getCreatedBy();
        AgentUserModel agentUserModel = agentUserService.getById(createdBy);
        String agentName;
        if (StrUtil.isNotBlank(agentUserModel.getParentId())) {
            agentName = userService.queryName(agentUserModel.getParentId());
        } else {

            agentName = userService.queryName(agentUserModel.getId());
        }
        openSkuVo.setAgentName(agentName);
        openSkuVo.setCustomService(userService.queryName(agentUserModel.getId()));
        //获取试题总数 已做题数
        AtomicInteger questionCount = new AtomicInteger();
        AtomicInteger done = new AtomicInteger();
        categoryIds.parallelStream().forEach(c -> {
            //获取科目下的题数
            int count = categoryService.fetchCategoryQuestionCount(c);
            questionCount.addAndGet(count);
            //获取用户科目下所做题数
            RecordsCountRecordDto categoryCountRecordsResult = recordsGetDataService.getCategoryCountRecordsResult(orderModel.getUserId(), packageModel.getIndustryId(), packageModel.getExamId(), c);
            done.addAndGet(categoryCountRecordsResult.getDc());
        });
        openSkuVo.setQuestionCount(questionCount.get());
        openSkuVo.setDone(done.get());
        return openSkuVo;
    }


    // 需要传入分享人id
    @Operation(summary = "创建订单")
    @PostMapping("/create-order")
    @RequireContext({com.caishi.lkx.common.context.Role.APP, com.caishi.lkx.common.context.Role.PC})
    public OrderModel createOrder(@RequestParam List<String> skuIds, @RequestParam(required = false) String remark, @RequestParam(required = false) String recommendId) {
        String userId = userContext.currentUserId();
        Long groupId = context.getGroupId();
        List<OrderDetailModel> detailModels = skuIds.stream().map(skuId -> {
            OrderDetailModel detail = OrderDetailModel.builder()
                    .entityId(skuId)
                    .entityType(AssetsType.product)
                    .build();
            detail.setCreatedBy(userId);
            return detail;
        }).toList();
        // 解析recommendId
        String recommend = null;
        if (recommendId != null) {
//            "05ee4559b3db2ab9a1f5d86a316c90d4" "9b91f1963cf54dde82293db9295bb89b"
//            var aes = new AES(Mode.ECB, Padding.PKCS5Padding, EncryptionKerayHandlerMethodArgumentResolverConfig.aesKey.getBytes(StandardCharsets.UTF_8));
//            recommend = ContextInterceptor.AES.decryptStr(recommendId);
            recommend = EncryptionKerayHandlerMethodArgumentResolverConfig.decrypt(recommendId);
            if (userContext.currentUserId().equals(recommend)) {
                return orderService.createOrder(userId, userId, OrderType.onlineOrder, detailModels, remark, null, null, groupId, recommend);
            }
//            recommend = EncryptionKerayHandlerMethodArgumentResolverConfig.rsa.decryptStr(recommendId, KeyType.PrivateKey);
        }

        return orderService.createOrder(userId, userId, OrderType.onlineOrder, detailModels, remark, null, null, groupId);
    }

    @Operation(summary = "预览订单")
    @PostMapping("/preview-order")
    public OrderModel previewOrder(@RequestParam List<String> skuIds) {
        String userId = userContext.currentUserId();
        Long groupId = userContext.currentGroupId();
        List<OrderDetailModel> detailModels = skuIds.stream().map(skuId -> {
            OrderDetailModel detail = OrderDetailModel.builder()
                    .entityId(skuId)
                    .entityType(AssetsType.product)
                    .build();
            detail.setCreatedBy(userId);
            return detail;
        }).toList();
        return orderService.previewOrder(userId, userId, OrderType.onlineOrder, detailModels, "", null, null, null, groupId);
    }


    @Operation(summary = "获取用户订单")
    @GetMapping("/user-order")
    @RequireContext(com.caishi.lkx.common.context.Role.PC)
    public IPage<Object> userOrder(
            @ModelAttribute Page<OrderModel> pager,
            OrderStatus orderStatus,
            PaymentStatus paymentStatus,
            @RequestParam(value = "detail", defaultValue = "false") Boolean detail,
            @RequestParam(value = "payDetail", defaultValue = "false") Boolean payment
    ) {
        List<OrderItem> orders = pager.orders();
        orders.add(OrderItem.desc("created_time"));
        pager.setOrders(orders);
        var page = orderService.search(clearReturnEmptyStr(
                MapUtil.<String, Object>builder()
                        .put("orderStatus", orderStatus)
                        .put("paymentStatus", paymentStatus)
                        .put("noOrderStatus", OrderStatus.delete)
                        .put("userId", userContext.currentUserId())
                        .put("type", OrderType.onlineOrder)
                        .put("groupId", userContext.currentGroupId())
                        .build()
        ), pager, true);

        IPage<Object> result = page.convert(v -> v);
        return result.setRecords(page.getRecords()
                .parallelStream()
                .map(vo -> {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> res = (HashMap<String, Object>) orderListProcess(vo, false, payment, false, detail, false, false);
                    OrderDetailModel orderDetailModel = orderDetailService.selectFirst(Wrappers.<OrderDetailModel>lambdaQuery().eq(OrderDetailModel::getOrderId, res.get("id")));
                    PackageSkuModel packageSkuModel = packageSkuService.getById(orderDetailModel.getEntityId());
                    if (null != packageSkuModel) {
                        res.put("categoryId", packageSkuModel.getCategoryIds());
                        res.put("industryId", packageSkuModel.getIndustryId());
                        res.put("examId", packageSkuModel.getExamId());
                    }

                    return (Object) res;
                })
                .toList());
    }


    @Operation(summary = "获取用户订单数量")
    @GetMapping("/user-order-count")
    public Long userOrderCount(OrderStatus orderStatus, PaymentStatus payStatus) {
        return orderService.searchCount(
                clearReturnEmptyStr(
                        MapUtil.<String, Object>builder()
                                .put("orderStatus", orderStatus)
                                .put("noOrderStatus", OrderStatus.delete)
                                .put("userId", userContext.currentUserId())
                                .put("type", OrderType.onlineOrder)
                                .build()
                )
        );
    }

    @Operation(summary = "用户订单状态变更")
    @PostMapping("/status-change")
    public void orderCancel(String orderId, OrderStatus status) {
        if (StrUtil.isNotBlank(orderId)) {
            orderService.userOrderStatusChange(orderId, status);
        }
    }

    @Operation(summary = "修改订单金额")
    @PostMapping("/price-change")
    public void priceChange(String orderId, BigDecimal price) {
        if (StrUtil.isNotBlank(orderId)) {
            orderService.update(null, Wrappers.<OrderModel>lambdaUpdate().set(OrderModel::getPrice, price.multiply(BigDecimal.valueOf(100))).eq(OrderModel::getId, orderId));
        }
    }

    @Operation(summary = "用户订单删除")
    @DeleteMapping("/user-delete")
    public void orderDeleteByUserId(@RequestParam String orderId) {
        if (StrUtil.isNotBlank(orderId)) {
            orderService.userOrderStatusChange(orderId, OrderStatus.delete, userContext.currentUserId());
        }
    }

    @Operation(summary = "订单申请退款")
    @PostMapping("/apply-refund")
    public void orderApplyRefund(@RequestParam String orderId) throws BizException {
        orderService.applyRefund(orderId, userContext.currentUserId(), true, false);
    }

    // 管理端订单退款
    @PostMapping("/order-skip-check-refund")
    @Roles(Role.admin)
    @Transactional(rollbackFor = Exception.class)
    public void orderSkipCheckRefund(@RequestParam String id, BigDecimal refundPrice) throws BizException {
        orderService.applyRefund(id, userContext.currentUserId(), false, true);
        orderService.orderConfirmRefund(id, refundPrice.multiply(BigDecimal.valueOf(100)).longValue());
    }


    @Operation(summary = "用户产品激活码开课")
    @PostMapping("/user/open-sku-code")
    @Parameters({
            @Parameter(name = "code", required = true, description = "激活码")
    })
    @Transactional(rollbackFor = Exception.class)
    public PackageSkuModel userOpenByCode(@RequestParam String code, HttpServletRequest request) {
        return generateActivationCode(code, ProductCodeType.product, null, request);
    }

    @Operation(summary = "用户套餐激活码开课")
    @PostMapping("/user/open-sku-code-package")
    @Parameters({
            @Parameter(name = "code", required = true, description = "激活码")
    })
    @Transactional(rollbackFor = Exception.class)
    public PackageSkuModel userOpenByCodePackage(@RequestParam String code, @RequestParam String skuId, HttpServletRequest request) {
        return generateActivationCode(code, ProductCodeType.packages, skuId, request);
    }


    private PackageSkuModel generateActivationCode(String code, ProductCodeType productCodeType, String activeSkuId, HttpServletRequest request) {
        String userId = userContext.currentUserId();
        Long groupId = context.getGroupId();
        //支付金额
        long price = 0;
        try {
            RedissonLock.lock(code + ":agent-open-sku-code", k -> {
                var codeModel = productCodeService.getAndCheckByCode(code);
                var skuId = activeSkuId;
                if (productCodeType == ProductCodeType.product) {
                    skuId = codeModel.getSkuId();
                }
                List<OrderDetailModel> details = new ArrayList<>();
                var detail = OrderDetailModel.builder()
                        .entityId(skuId)
                        .entityType(AssetsType.product)
                        .build();
                detail.setCreatedBy(userId);
                details.add(detail);
                OrderModel orderModel = orderService.createOrder(userId, userId, OrderType.userCode, details, null, PaymentType.activationCode, null, price, skuId, groupId);
                codeModel.setSkuId(skuId);
                codeModel.setUserTime(LocalDateTime.now());
                codeModel.setUserId(userId);
                codeModel.setStatus(ProductCodeStatus.used);
                productCodeService.simpleUpdate(codeModel);
                // 生成支付单
                paymentService.getPaySign(codeModel.getUserId(), PaymentType.activationCode, orderModel.getId(), HttpContextUtil.currentDeviceType(request), code, null);
                // 订单直接支付完成
                paymentService.paySuccess(orderModel.getId(), null,null);
                orderService.orderSuccess(orderModel.getId());
            });
            String skuId = productCodeService.getById(code).getSkuId();
            var sku = packageSkuService.selectOne(Wrappers.<PackageSkuModel>lambdaQuery().eq(PackageSkuModel::getId, skuId).select(PackageSkuModel::getIndustryId, PackageSkuModel::getExamId, PackageSkuModel::getCategoryIds, PackageSkuModel::getName));
            sku.setExamName(examService.getById(sku.getExamId()).getName());
            return sku;
        } catch (Exception e) {
            log.error("用户激活码开课失败:userId:{},code:{}", userId, code);
            Throwable cause = e.getCause();
            String message = e.getMessage();
            if (cause instanceof BizRuntimeException) {
                message = e.getCause().getMessage();
            }
            throw new BizRuntimeException(message);
        }
    }

    @Operation(summary = "代理商提码 skuid")
    @PostMapping("/agent/open-sku-code")
    @Parameters({
            @Parameter(name = "packageId", required = true, description = "packageId"),
            @Parameter(name = "skuId", required = true, description = "skuId"),
            @Parameter(name = "email", description = "邮箱")})
    @Transactional(rollbackFor = Exception.class)
    public Boolean agentOpenSkuCode(@RequestParam String packageId, @RequestParam String skuId, HttpServletRequest request) {
        String userId = userContext.currentUserId();
        this.generateActivationCode(packageId, ProductCodeType.product, skuId, userId, request);
        return true;
    }

    @Operation(summary = "h5代理商提码 skuid")
    @PostMapping("/agent/open-h5-sku-code")
    @Parameters({
            @Parameter(name = "packageId", required = true, description = "packageId"),
            @Parameter(name = "skuId", required = true, description = "skuId"),
            @Parameter(name = "email", description = "邮箱")})
    @Transactional(rollbackFor = Exception.class)
    public List<String> agentOpenSkuCode(@RequestParam String packageId, @RequestParam String skuId, @RequestParam(defaultValue = "1") Integer count, HttpServletRequest request) {
        List<String> codes = new ArrayList<>();
        String userId = userContext.currentUserId();
        for (int i = 0; i < count; i++) {
            ProductCodeModel productCodeModel = this.generateActivationCode(packageId, ProductCodeType.product, skuId, userId, request);
            codes.add(productCodeModel.getId());
        }
        return codes;
    }

    @Operation(summary = "代理商套餐提码")
    @PostMapping("/agent/open-sku-code-package")
    @Parameters({
            @Parameter(name = "packageId", required = true, description = "packageId"),
    })
    @Transactional(rollbackFor = Exception.class)
    public List<String> agentOpenSkuCode(@RequestParam String packageId, @RequestParam(defaultValue = "1") Integer count, HttpServletRequest request) {
        List<String> codes = new ArrayList<>();
        String userId = userContext.currentUserId();
        for (int i = 0; i < count; i++) {
            ProductCodeModel productCodeModel = this.generateActivationCode(packageId, ProductCodeType.packages, null, userId, request);
            codes.add(productCodeModel.getId());
        }
        return codes;
    }

    @Transactional
    public ProductCodeModel generateActivationCode(String packageId, ProductCodeType type, String skuId, String userId, HttpServletRequest request) {
        //sku金额
        var packageModel = packageService.getById(packageId);
        //代理商折扣
        AgentUserModel mainInfo = agentUserService.getMainInfo(userId);

        ProductCodeModel codeModel = new ProductCodeModel();
        codeModel.setCreatedBy(userId);
        codeModel.setId(UUIDUtil.generateUUIDByTimestamp());
        codeModel.setPackageId(packageId);
        codeModel.setIndustryId(packageModel.getIndustryId());
        codeModel.setExamId(packageModel.getExamId());
        codeModel.setSkuId(skuId);
        codeModel.setType(type);
        //支付金额
        long price = 0L;

        try {
            if (type == ProductCodeType.packages) {
                price = new BigDecimal(packageModel.getAgentPrice()).multiply(mainInfo.getDiscount()).longValue();
            } else {
                PackageSkuModel skuModel = packageSkuService.getById(skuId);
                price = new BigDecimal(skuModel.getAgentPrice()).multiply(mainInfo.getDiscount()).longValue();
            }

            long finalPrice = price;

            RedissonLock.lock(userId + ":agent-open-code", k -> {
                // 创建订单
                OrderModel orderModel = createOrder(packageId, userId, finalPrice, codeModel);

                productCodeService.insert(codeModel);
                // 生成支付单
                paymentService.getPaySign(mainInfo.getId(), PaymentType.agentpay, orderModel.getId(), HttpContextUtil.currentDeviceType(request), null, null);
                // 订单直接支付完成
                paymentService.paySuccess(orderModel.getId(), null,null);
                orderService.orderSuccess(orderModel.getId());
            });
        } catch (Exception e) {
            Throwable cause = e.getCause();
            String message = e.getMessage();
            if (cause instanceof BizRuntimeException) {
                message = e.getCause().getMessage();
            }
            log.error("代理商提码失败:userId:{},payUserId:{},message:{}", userId, mainInfo.getId(), message);
            throw new BizRuntimeException("提码失败:" + message);
        }
        return codeModel;
    }

    private OrderModel createOrder(@RequestParam String packageId, String userId, Long price, ProductCodeModel codeModel) {
        OrderDetailModel detail = OrderDetailModel.builder()
                .entityId(codeModel.getId())
                .entityType(AssetsType.skuCode)
                .build();
        detail.setCreatedBy(userId);
        OrderModel orderModel = orderService.createOrder(userId, userId, OrderType.openCode, Collections.singletonList(detail), null, PaymentType.agentpay, null, price, packageId, null);
        codeModel.setOrderId(orderModel.getId());
        return orderModel;
    }

    @PostMapping("/auth-sku")
    public Boolean currentHaveAuthSku(@RequestParam List<String> ids) throws BizException {
//        List<PackageSkuModel> packageSkuModels = packageSkuService.selectBatchIds(ids);
//        Long userGroupId = userContext.currentGroupId();
//        for (PackageSkuModel packageSkuModel : packageSkuModels) {
//            List<Long> categoryIds = packageSkuModel.getCategoryIds();
//            for (Long categoryId : categoryIds) {
//                try {
////                    if (groupService.groupsHaveCategoryPermission(userGroupId, null, categoryId, userContext.currentTokenData().getClientType())) {
////                        return true;
////                    }
//                } catch (BizException e) {
//                    return false;
//                }
//
//            }
//        }
        return false;
    }

    @GetMapping("/index-channel-count")
    @Roles({Role.onlyLogin})
    @RequireContext(com.caishi.lkx.common.context.Role.CHANNEL)
    public Map<String, Object> indexCount(@RequestParam(value = "all", defaultValue = "true") Boolean all) {
        Long groupId = context.getGroupId();
        Double totalPrice = orderService.selectSum(Wrappers.<OrderModel>lambdaQuery().select(OrderModel::getPrice).eq(!all, OrderModel::getGroupId, groupId).in(OrderModel::getOrderStatus, Arrays.asList(OrderStatus.success, OrderStatus.waitSign))) / 100;
        Double todayPrice = orderService.selectSum(Wrappers.<OrderModel>lambdaQuery().select(OrderModel::getPrice).eq(!all, OrderModel::getGroupId, groupId).in(OrderModel::getOrderStatus, Arrays.asList(OrderStatus.success, OrderStatus.waitSign)).like(OrderModel::getCreatedTime, LocalDate.now())) / 100;
        return MapUtil.<String, Object>builder().put("totalPrice", totalPrice).put("todayPrice", todayPrice).build();
    }

    @GetMapping("/index-channel-line")
    @Roles({Role.onlyLogin})
    @RequireContext(com.caishi.lkx.common.context.Role.CHANNEL)
    public List<Map> indexLine(@RequestParam("startTime") LocalDateTime startTime, @RequestParam("endTime") LocalDateTime endTime, @RequestParam("timeType") String timeType, @RequestParam(value = "all", defaultValue = "true") Boolean all) {
        Map<String, Object> map = new HashMap<>();
        if (!all) {
            map.put("groupId", context.getGroupId());
        }

        String format = "%Y-%m-%d";
        if ("year".equals(timeType)) {
            format = "%Y-%m";
        }
        map.put("timeType", format);
        map.put("startTime", startTime);
        map.put("endTime", endTime);
        map.put("orderStatues", Arrays.asList(OrderStatus.success, OrderStatus.waitSign));
        return orderService.priceCountLine(map);
    }
}
